﻿using System;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Windows.Forms;
using XLETL.Model;
using XLETL.Common;

namespace XLETL.TransformationPluginManager
{
    /// <summary>
    /// Provide methods to refersh the Plugin Store. This class cannot be inherited
    /// </summary>
    public sealed class PluginStoreManager
    {
        /// <summary>
        /// The path to the plugin store XML file
        /// </summary>
      public static readonly string PluginStoreFile = Path.Combine(GlobalCache.PLUGIN_STORAGE_FOLDER, "PluginStore.xml");

        /// <summary>
        /// The base directory to search for installed plugins
        /// </summary>
        public static readonly string PluginsDirectory = GlobalCache.PLUGIN_STORAGE_FOLDER;

        private static XmlSerializer serializer = new XmlSerializer(typeof(PluginStore));        

        /// <summary>
        /// Loads the plugins set up in the plugin store
        /// </summary>
        /// <returns></returns>
        public static PluginStore LoadPuginStore()
        {
            if (!File.Exists(PluginStoreFile))
                return null;

            StreamReader reader = new StreamReader(PluginStoreFile);
            PluginStore pluginStore = (PluginStore)serializer.Deserialize(reader);
            reader.Close();

            return pluginStore;
        }

        /// <summary>
        /// Refreshes the plugin store
        /// </summary>
        /// <returns></returns>
        public static PluginStore RefreshPuginStore()
        {
            PluginStore pluginStore = new PluginStore();

            string[] pluginDirs = Directory.GetDirectories(PluginsDirectory);
            int[] cumulativeFileCount = new int[pluginDirs.Length];
            int totalFiles = 0;
            int currentFileCount = 0;
            for (int d = 0; d < pluginDirs.Length; d++)
            {
                currentFileCount = Directory.GetFiles(pluginDirs[d]).Length;
                cumulativeFileCount[d] = currentFileCount;
                if (d > 0)
                {
                    cumulativeFileCount[d] = cumulativeFileCount[d] + cumulativeFileCount[d - 1];
                }
                totalFiles += currentFileCount;
            }
            cumulativeFileCount[0] = 0;

            for (int d = 0; d < pluginDirs.Length; d++)
            {
                string[] files = Directory.GetFiles(pluginDirs[d]);
                LoadPluginsInDirectory(ref pluginStore, pluginDirs[d], files, totalFiles, cumulativeFileCount[d]);
            }            

            // Save the new plugin store
            XmlSerializer w = new XmlSerializer(typeof(PluginStore));
            StreamWriter storeFile = new StreamWriter(PluginStoreFile);
            w.Serialize(storeFile, pluginStore);
            storeFile.Close();

            return pluginStore;
        }

        /// <summary>
        /// Inspects assemblies and executables in the specified directory
        /// using reflection and searches for controls with the PluginAttribute
        /// marked against its type. Updates the plugin store with discovered
        /// plugins
        /// </summary>
        /// <param name="pluginStore"></param>
        /// <param name="directory"></param>
        /// <param name="files"></param>
        private static void LoadPluginsInDirectory(ref PluginStore pluginStore, string directory, string[] files, int totalFiles, int currentCumulate)
        {
            for (int i = 0; i < files.Length; i++)
            {
                string file = files[i];
                // Process only dll and exe files
                if (file.EndsWith(".dll") || file.EndsWith(".exe"))
                {
                    try
                    {
                        Assembly a = Assembly.LoadFile(file);
                        Type[] types = a.GetTypes();
                        foreach (Type t in types)
                        {
                            if (t.IsSubclassOf(typeof(Control)))
                            {
                                object[] pluginAttributes = t.GetCustomAttributes(typeof(PluginAttribute), false);
                                if (null != pluginAttributes && pluginAttributes.Length > 0)
                                {
                                    PluginAttribute pluginAttribute = (PluginAttribute)pluginAttributes[0];                                    

                                    PluginInfo currentPlugin = new PluginInfo()
                                    {
                                        Name = pluginAttribute.Name,
                                        Description = pluginAttribute.Description,
                                        Type = t.FullName,
                                        AssemblyFile = file.Replace(AppDomain.CurrentDomain.BaseDirectory, ""),
                                        InstallPath = directory.Replace(AppDomain.CurrentDomain.BaseDirectory, ""),
                                        Target = pluginAttribute.Target
                                    };

                                    pluginStore.Plugins.Add(currentPlugin);                                    
                                }
                            }
                        }
                    }
                    catch { }
                }
            }
        }
    }
}

